package bancosys.tec.persist.hibernate.lazy;

import java.beans.PropertyDescriptor;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Hibernate;
import org.hibernate.SessionFactory;
import org.hibernate.metadata.ClassMetadata;

/**
 * Classe de utilidade para propriedades Lazy.
 */
public final class LazyUtils {

    private static Log log = LogFactory.getLog(LazyUtils.class);

    /**
     * Construtor.
     */
    private LazyUtils() {
        // vazio
    }

    /**
     * Inicializa as propriedades Lazy.
     * 
     * @param sessionFactory a sessionFactory.
     * @param target o objeto com as propriedades alvo.
     */
    public static void initializeLazyProperties(SessionFactory sessionFactory, Object target) {
        doInitializeLazyProperties(sessionFactory, target, new HashMap<Object, Object>());
    }

    /**
     * Realiza a iniicialização das propriedades Lazy.
     * 
     * @param sessionFactory a sessionFactory.
     * @param target o objeto com as propriedades alvo.
     * @param recursion mapa para recursão.
     */
    private static void doInitializeLazyProperties(SessionFactory sessionFactory, Object target, Map<Object, Object> recursion) {
        ClassMetadata metadata = sessionFactory.getClassMetadata(getRealClass(target));
        List<String> hibernateProperties = null;
        if (metadata == null) {
            log.debug("Não localizado metadata para objeto " + target.getClass() + " tentando inicializar todas as propriedades.");
        } else {
            hibernateProperties = Arrays.asList(metadata.getPropertyNames());
        }

        log.debug("Executando initializeLazyProperties para classe: " + target.getClass());

        PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(target.getClass());
        for (PropertyDescriptor descriptor : descriptors) {
            log.debug("Avaliando property: " + descriptor.getName());

            if (metadata != null) {
                if (!hibernateProperties.contains(descriptor.getName())) {

                    log.debug("Property " + descriptor.getName() + " não é persistente.");
                    continue;
                }

            }

            Object propValue = null;
            boolean initialized = true;
            try {
                propValue = PropertyUtils.getProperty(target, descriptor.getName());
                initialized = Hibernate.isInitialized(propValue);
            } catch (Exception e) {
                log.debug("ATENÇÃO:: Ignorando a propriedade " + descriptor.getName());
            }

            if (initialized) {
                log.debug("Propriedade já inicializada. Ignorando.");
            } else {
                log.debug("Propriedade NÃO inicializada. Execuando inicialização.");
                // Inicializando lazy
                Hibernate.initialize(propValue);
                log.debug("Propriedade inicializada com sucesso.");
                LazyUtils.doInitializeLazyProperties(sessionFactory, propValue, recursion);
            }
        }
        log.debug("initializeLazyProperties finalizado.");
    }

    /**
     * Devolve o nome verdadeiro da classe (sem proxy) do objeto.
     * 
     * @param target o objeto
     * @return o nome da classe.
     */
    private static Class<?> getRealClass(Object target) {
        if (target.getClass().getName().indexOf("$$") > 0) {
            return target.getClass().getSuperclass();
        } else {
            return target.getClass();
        }
    }

}
